#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time	: 2024/2/25 21:50
# @Author  : jize.liu
# @File	: Maya_BatchOutput.py
# @声明	 : 版权由 刘继泽 所有,如有疑问，需要帮助请联系刘继泽

import os
import json
import xlwt
import logging
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QCheckBox, QVBoxLayout, QSplitter,QMessageBox
from PySide6.QtCore import *
from Cutting_Video_ui import Ui_Cutting_Video_Win
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.audio.io.AudioFileClip import AudioFileClip
from collections import OrderedDict

class FileProcessor(QThread):
	"""
	进度条更新线程类，用于在后台更新进度条状态。
	"""
	progressChanged = Signal(int)
	completionSignal = Signal()  # 新增的信号，用于完成
	errorSignal = Signal(str)  # 新增的信号，用于错误
	def __init__(self, total_clips, edl_list,frames,output_folder, file_name, file_extension):
		"""
		初始化进度条更新线程。
		
		:param total_clips: 需要处理的视频片段总数
		:param output_folder: 输出文件夹路径
		:param file_name: 输出文件的基础名称
		:param file_extension: 输出文件的扩展名
		:param parent: 父对象
		"""
		super().__init__()
		self.total_work = total_clips
		self.edl_list = edl_list
		self.frames = frames
		self.output_folder = output_folder
		self.file_name = file_name
		self.file_extension = file_extension

	def run(self):
		"""
		线程运行入口，用于处理视频剪辑并更新进度条。
		"""
		try:
			if not self.total_work:
				return  # 增加边界条件检查
			for c_video, clip_info in enumerate(self.total_work):
				c = (c_video + 1)
				progress = int(c / len(self.total_work) * 100)
				if self.file_extension == ".mp4":
					short_number = clip_info["short"]
					video_clip = clip_info["video_clip"]
					output_path = os.path.join(self.output_folder, f"{self.file_name}_{short_number[0]}_{short_number[1]}{self.file_extension}")
					video_clip.write_videofile(output_path, bitrate='2500k')
					self.progressChanged.emit(progress)
				elif self.file_extension == ".wav":
					short_number = clip_info["short"]
					audio_clip = clip_info["audio_clip"]
					output_path = os.path.join(self.output_folder, f"{self.file_name}_{short_number[0]}_{short_number[1]}{self.file_extension}")
					audio_clip.write_audiofile(output_path, buffersize=2500, bitrate=None)
					self.progressChanged.emit(progress)
			self.Excel_Export()
		except Exception as e:
			self.errorSignal.emit("错误！请检查输入视频和EDL是否正确，错误信息为："+ str(e))  # 正确发射错误信号，这里假设handle_error是FileProcessor的一个方法
	def Excel_Export(self):
		json_list = []
		for edl_dict in range(len(self.edl_list)):
			short = self.edl_list[edl_dict]["short"]
			start = self.edl_list[edl_dict]["start_frame"]
			end = self.edl_list[edl_dict]["end_frame"]
			start_frame = int(start[-3]) * 60 * self.frames  + int(start[-2]) * self.frames  + int(start[-1])
			end_frame = int(end[-3]) * 60 * self.frames  + int(end[-2]) * self.frames  + int(end[-1])
			frame = end_frame - start_frame + 100
			all_frame = end_frame - start_frame
			info_json = OrderedDict()
			info_json["short"] = short
			info_json["start"] = 101
			info_json["end"] = frame
			info_json["frame"] = all_frame
			json_list.append(info_json)
				#self.progress_Bar(edl_dict,len(edl_list))
			jsonFile = os.path.join(self.output_folder, f"{self.file_name}.json")
			with open(jsonFile, "w") as f:
				json.dump(json_list, f)
			self.judgeFile(jsonFile.replace('.json', '.xls'))
			with open(jsonFile, 'r') as f:
				jsonInfos = json.load(f)
			workbook = xlwt.Workbook()
			N2 = 0
			tt = 0
			C_sheet = workbook.add_sheet(self.file_name, cell_overwrite_ok=True)
			C_sheet.write(0, 0, 'short')
			C_sheet.write(0, 1, 'start')
			C_sheet.write(0, 2, 'end')
			C_sheet.write(0, 3, 'frame')
			for jsonInfo in jsonInfos:
				N2 += 1 + tt
				C_sheet.write(N2, 0, jsonInfo['short'])
				C_sheet.write(N2, 1, jsonInfo['start'])
				C_sheet.write(N2, 2, jsonInfo['end'])
				C_sheet.write(N2, 3, jsonInfo['frame'])
		workbook.save(jsonFile.replace('.json', '.xls'))
	def judgeFile(self,xlsPath):
		try:
			if os.path.isfile(xlsPath):
				os.remove(xlsPath)
		except Exception as e:
			return(QMessageBox.critical(self,u'错误', xlsPath + u'\n表格文件被占用无法删除,请关掉文件,如需保管,请另存为'))
class Cutting_Video_Function(Ui_Cutting_Video_Win, QMainWindow):
	"""
	视频剪辑功能类，实现了视频剪辑的UI界面和逻辑处理。
	"""
	def __init__(self):
		"""
		初始化视频剪辑功能类。
		"""
		super(Cutting_Video_Function, self).__init__()
		self.setupUi(self)
		self.Out_Button.clicked.connect(self.Out_ButtonClicked)
		self.EDL_PathButton.clicked.connect(self.open_edl_file)
		self.MP4_PathButton.clicked.connect(self.open_mp4_file)
		self.Out_PathButton.clicked.connect(self.select_output_folder)


	def on_completion(self):
		# 处理完成逻辑
		pass

	def on_error(self, error_message):
		# 处理错误逻辑
		QMessageBox.critical(self, "错误", error_message)
	def handle_error(self, error_message):
		"""
		处理并显示错误信息。
		
		:param error_message: 错误信息字符串
		"""
		print(f"Error: {error_message}")  # 可以在这里添加更复杂的错误处理逻辑
	def ExcelCut_perform(self,FileName,edl_list,frames,clip_list,Out_savePath):
		"""
		执行Excel剪辑任务。
		:param FileName: 输出文件名
		:param edl_list: EDL列表
		:param frames: 帧数
		:param clip_list: 剪辑列表
		:param Out_savePath: 输出路径
		"""
		list = [frames,".xls"]
		self.thread_pool.submit(self.perform_cutting, FileName, edl_list, Out_savePath, list)

		
	def videoCut_perform(self, FileName, edl_list,frames,clip_list, Out_savePath):
		"""
		执行视频剪辑任务。
		
		:param FileName: 输出文件名
		:param clip_list: 视频剪辑列表
		:param Out_savePath: 输出路径
		"""
		self.perform_cutting(FileName,edl_list,frames,clip_list, Out_savePath, '.mp4')


	def audioCut_perform(self, FileName,edl_list, frames,clip_list, Out_savePath):
		"""
		执行音频剪辑任务。
		
		:param FileName: 输出文件名
		:param clip_list: 音频剪辑列表
		:param Out_savePath: 输出路径
		"""
		self.perform_cutting(FileName, edl_list,frames,clip_list, Out_savePath, '.wav')

	def perform_cutting(self, FileName, edl_list,frames,clip_list, Out_savePath, file_extension):
		"""
		在新线程中执行实际的剪辑操作。
		
		:param FileName: 输出文件名
		:param clip_list: 剪辑列表
		:param Out_savePath: 输出路径
		:param file_extension: 输出文件扩展名
		"""
		self.thread = FileProcessor(clip_list,edl_list,frames, Out_savePath, FileName, file_extension)
		self.thread.progressChanged.connect(self.update_progress)
		self.thread.completionSignal.connect(self.on_completion)  # 连接完成信号
		self.thread.errorSignal.connect(self.on_error)  # 连接错误信号
		self.thread.start()
		#thread.wait()
	def update_progress(self, value):
		"""
		更新UI上的进度条。
		:param value: 进度值
		"""

		self.progressBar.setValue(value)
		if value == 100:
			self.progressBar.setFormat('完成')
	def open_edl_file(self):
		edl_path = self.get_file("选择EDL文件", "*.edl")
		if edl_path[0]:
			self.EDL_Line.setText(edl_path[0])
	def open_mp4_file(self):
		mp4_path = self.get_file("选择MP4文件", "*.mp4")
		if mp4_path[0]:
			self.MP4_Line.setText(mp4_path[0])
	def select_output_folder(self):
		out_folder = self.get_directory()
		if out_folder:
			self.Out_Line.setText(out_folder)
	def get_directory(self):
		return (QFileDialog.getExistingDirectory(QMainWindow(), "选择文件夹"))
	def get_file(self, title, filter=""):
		return (QFileDialog.getOpenFileName(QMainWindow(), title, "", filter))

	def validate_path(self, path):
		if not os.path.exists(path):
			QMessageBox.critical(self, "错误", "路径不存在")
			return (False)
		return (True)

	def process_edl(self, edl_path):
		edl_list = []
		try:
			with open(edl_path, "r") as f:
				for line in f:
					# 检查是否为新剪辑开始标记行
					if 'AX' in line and "V" in line:
						text_time = line.split()
						start = text_time[-2].split(":")
						end = text_time[-1].split(":")
					# 解析时间码信息，这里以 AX 和 V 作为标记
					elif '* FROM CLIP NAME:' in line:
						if ".mp4" not in line:
							text_name = line.split()
							name = text_name[-1]
							edl_dict = {"start_frame": start, "end_frame": end, "short": name}
							edl_list.append(edl_dict)
		except FileNotFoundError:
			return (QMessageBox.critical(self, "错误", f"打开EDL文件时出现错误：文件找不到"))
		except Exception as e:
			return(QMessageBox.critical(self, "错误", f"读取EDL文件时出现错误：{str(e)}"))
		return (edl_list)
	def Out_ButtonClicked(self):
		self.progressBar.setValue(0)
		# 获取用户在UI上输入的EDL文件路径、MP4文件路径和输出目录路径
		edl_path = self.EDL_Line.text()#.strip()  # 去除首尾空白字符确保路径准确性
		mp4_path = self.MP4_Line.text()#.strip()
		out_folder = self.Out_Line.text()#.strip()

		# 验证输入路径的有效性
		if not self.validate_path(edl_path) or not self.validate_path(mp4_path) or not self.validate_path(out_folder):
			# 如果任一路径无效，则显示错误消息并退出函数
			return(QMessageBox.warning(self, "警告", "无效的路径，请重新输入有效的文件或目录路径！"))
			
		# 处理并解析EDL文件，获取剪辑列表
		edl_list = self.process_edl(edl_path)
		if not len(edl_list):
			# 如果EDL文件解析失败或无有效数据，则显示错误消息并退出函数
			return(QMessageBox.warning(self, "警告", "无法正确解析EDL文件，请检查文件内容是否正确！"))

		FileName =  (os.path.basename(self.EDL_Line.text())).split('.')[0]
		#FileName =  edl_path.split('.')[0]
		self.VideoCut = VideoFileClip(self.MP4_Line.text())
		self.AudioCut = AudioFileClip(self.MP4_Line.text())
		frames = int(self.VideoCut.fps)
		clip_list = []
		for i in edl_list:
			start = i["start_frame"]
			end = i["end_frame"]
			short = i["short"]
			sp_short = short.split("_")
			# print start
			start_frame = float(start[-2]) + float(start[-1]) / frames
			end_frame = float(end[-2]) + (float(end[-1]) - 0.5) / frames
			# print start_frame,end_frame
			video_clip = self.VideoCut.subclip((float(start[-3]), start_frame), (float(end[-3]), end_frame))
			audio_clip = self.AudioCut.subclip((float(start[-3]), start_frame), (float(end[-3]), end_frame))
			clip_dict = {"short": sp_short, "video_clip": video_clip, "audio_clip": audio_clip}
			clip_list.append(clip_dict)
		if self.video_check.isChecked() == 1:
			self.videoCut_perform( FileName,edl_list,frames,clip_list,self.Out_Line.text())

		elif self.audio_check.isChecked()== 1 :
			self.audioCut_perform(FileName,edl_list,frames,clip_list,self.Out_Line.text())

if __name__ == "__main__":
	import sys
	app = QApplication (sys.argv)
	#widget = QtWidgets.QWidget()
	window = Cutting_Video_Function()  # 修改
	window.show ()
	sys.exit (app.exec ())